package com.lingjtx.common.core.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 命令行调用工具
 */
public class CommandExecutor {

    private static final Logger log = LoggerFactory.getLogger(CommandExecutor.class);

    private static final long DEFAULT_TIMEOUT_MS = 60_000;

    private static final ExecutorService DEFAULT_EXECUTOR = new ThreadPoolExecutor(
            2, 10,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            r -> new Thread(r, "command-thread-" + StringUtil.getUuid()),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private ExecutorService executor = DEFAULT_EXECUTOR;
        private long timeout = DEFAULT_TIMEOUT_MS;
        private boolean printLogs = true;
        private CommandCallback callback;
        private List<String> commandList;

        public Builder command(String command) {
            this.commandList = parseCommand(command);
            return this;
        }

        public Builder command(List<String> commandList) {
            this.commandList = commandList;
            return this;
        }

        public Builder commandCallback(CommandCallback callback) {
            this.callback = callback;
            return this;
        }

        public Builder isPrintLogs(boolean printLogs) {
            this.printLogs = printLogs;
            return this;
        }

        public Builder timeout(long timeout) {
            this.timeout = timeout;
            return this;
        }

        public Builder executor(ExecutorService executor) {
            this.executor = executor;
            return this;
        }

        public Future<CommandResult> run() {
            if (commandList == null || commandList.isEmpty()) {
                throw new IllegalStateException("Command list cannot be null or empty.");
            }
            return runCommand(commandList, timeout, printLogs, callback, executor);
        }

        // ✅ 同步执行（封装 get）
        public CommandResult execute() throws ExecutionException, InterruptedException {
            return run().get();
        }

        // ✅ 异步执行（不关心回调）
        public void executeAsync() {
            run(); // fire and forget
        }

        // ✅ 异步执行（返回 CompletableFuture）
        public CompletableFuture<CommandResult> executeAsyncFuture() {
            CompletableFuture<CommandResult> future = new CompletableFuture<>();
            if (callback == null) {
                this.commandCallback(new CommandCallback() {
                    @Override
                    public void onLog(String str) {
                        log.info(str);
                    }

                    @Override
                    public void onSuccess(int exitCode, String output) {
                        future.complete(new CommandResult(exitCode, output));
                    }

                    @Override
                    public void onError(Throwable e) {
                        future.completeExceptionally(e);
                    }
                });
            }
            run(); // submit task
            return future;
        }

        public Future<?> streamReader(InputStream inputStream, Consumer<String> consumer) {
            return executor.submit(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        consumer.accept(line);
                    }
                } catch (IOException e) {
                    log.error("[CommandExecutor] 日志读取失败", e);
                    consumer.accept("[日志读取失败] " + e.getMessage());
                }
            });
        }
    }

    // ====================== 下面方法是极简调用，在没有构建 builder 的时候，直接调用 =====================

    public static CommandResult execute(List<String> commandList)
            throws IOException, InterruptedException, ExecutionException {
        return builder().command(commandList).run().get();
    }

    public static void executeAsync(String command) {
        executeAsync(parseCommand(command));
    }

    public static void executeAsync(List<String> commandList) {
        builder().command(commandList).run();
    }

    public static CompletableFuture<CommandResult> executeAsyncFuture(String command) {
        return executeAsyncFuture(parseCommand(command));
    }

    public static CompletableFuture<CommandResult> executeAsyncFuture(List<String> commandList) {
        CompletableFuture<CommandResult> future = new CompletableFuture<>();
        builder().command(commandList).commandCallback(new CommandCallback() {
            @Override
            public void onLog(String str) {
                log.info(str);
            }

            @Override
            public void onSuccess(int exitCode, String output) {
                future.complete(new CommandResult(exitCode, output));
            }

            @Override
            public void onError(Throwable e) {
                future.completeExceptionally(e);
            }
        }).run();
        return future;
    }

    private static Future<CommandResult> runCommand(List<String> commandList,
                                                    long timeoutMillis,
                                                    boolean printLogs,
                                                    CommandCallback callback,
                                                    ExecutorService executor) {
        return executor.submit(() -> {
            try {
                ProcessBuilder builder = new ProcessBuilder(commandList);
                builder.redirectErrorStream(true);

                Process process = builder.start();
                StringBuilder outputBuilder = new StringBuilder();

                Future<?> outputFuture = CommandExecutor.builder()
                        .executor(executor)
                        .streamReader(process.getInputStream(), line -> {
                            if (callback != null) {
                                callback.onLog(line);
                            }
                            outputBuilder.append(line).append(System.lineSeparator());
                        });

                boolean finished = process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS);
                outputFuture.get(); // 等待日志输出完成
                int exitCode = process.exitValue();
                String output = outputBuilder.toString();

                if (printLogs) {
                    log.info("[CommandExecutor] Output:\n{}", output);
                }

                if (!finished) {
                    process.destroyForcibly();
                    TimeoutException timeoutException = new TimeoutException("命令执行超时: " + String.join(" ", commandList));
                    if (callback != null) {
                        callback.onError(timeoutException);
                    }
                    throw timeoutException;
                }

                if (exitCode != 0) {
                    CommandException commandException = new CommandException("命令执行失败，退出码: " + exitCode + "\n输出日志:\n" + output);
                    if (callback != null) {
                        callback.onError(commandException);
                    }
                    throw commandException;
                }

                if (callback != null) {
                    callback.onSuccess(exitCode, output);
                }

                return new CommandResult(exitCode, output);
            } catch (Throwable e) {
                if (callback != null) {
                    try {
                        callback.onError(e);
                    } catch (Throwable callbackError) {
                        log.warn("[CommandExecutor] 回调 onError 异常：{}", callbackError.getMessage(), callbackError);
                    }
                }
                throw e;
            }
        });
    }

    private static List<String> parseCommand(String command) {
        List<String> parts = new ArrayList<>();
        Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(command);
        while (m.find()) {
            parts.add(m.group(1).replace("\"", ""));
        }
        return parts;
    }

    public record CommandResult(int exitCode, String output) {
        public boolean isSuccess() {
            return exitCode == 0;
        }
    }

    public interface CommandCallback {
        // 输出执行日志回调
        void onLog(String log);

        // 成功回调
        void onSuccess(int exitCode, String output);

        // 异常回调
        void onError(Throwable e);
    }

    public static class CommandException extends RuntimeException {
        public CommandException(String message) {
            super(message);
        }
    }

//    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        // 同步执行
//        CommandResult result = CommandExecutor.builder()
//                .command("docker ps")
//                .executeSync();
//        System.out.println(result.output());
//
//        // 异步执行（不关心结果）
//        CommandExecutor.builder()
//                .command("ls")
//                .executeAsync();
//
//        // 异步执行 + CompletableFuture
//        CommandExecutor.builder()
//                .command("java --version")
//                .executeAsyncFuture()
//                .thenAccept(res -> System.out.println(res.output()))
//                .exceptionally(ex -> {
//                    ex.printStackTrace();
//                    return null;
//                });
//    }
}